﻿namespace SharpTalk.Client.WpfApplication
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Reflection;
    using System.Threading;
    using System.Windows;
    using Core.Diagnostic;
    using Properties;
    using Services;
    using System.Globalization;

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static ITraceProvider _appTrace = null;
        private static string _appinfos = null;
        private static string _appName = null;
        private Mutex _mutex;

        /// <summary>
        /// Obtains the single instance Application TraceProvider
        /// </summary>
        public static ITraceProvider Trace
        {
            get { return _appTrace; }
        }
        /// <summary>
        /// Obtains Application name and version
        /// </summary>
        public static string ApplicationInfos
        {
            get { return _appinfos; }
        }
        /// <summary>
        /// Obtains the Application Title
        /// </summary>
        public static string ApplicationName
        {
            get { return _appName; }
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            _mutex = new Mutex(false, @"SharpTalk"); 
            if (!_mutex.WaitOne(0, false))
            {
                // Another instance is already running
                _mutex.Dispose();
                // Clean exit
                Environment.Exit(-1);
            }

            base.DispatcherUnhandledException += UnhandledException;

            // Initializes application trace provider
            _appTrace = ApplicationTraceProvider.CreateNew("SharpTalk.Client", GetSettingsSourceLevel(), new CustomTraceListener(GetLogFilePath()));

            SetApplicationInfos();

            Trace.TraceInfo("Starting application, {0}", _appinfos);
            Trace.TraceInfo("Default Culture {0}", CultureInfo.CurrentUICulture);

            if (!string.IsNullOrEmpty(Settings.Default.Culture) && !string.Equals(CultureInfo.CurrentUICulture.Name, Settings.Default.Culture, StringComparison.InvariantCultureIgnoreCase))
                ApplyCulture(Settings.Default.Culture);

            ParseArguments(e.Args);

            if (string.IsNullOrEmpty(Settings.Default.LocalServiceUri))
                try
                {
                    SetLocalUri();
                }
                catch (System.Exception exp)
                {
                    Trace.TraceError("The attempt to set the local service uri address has raised an exception of type \"{0}\". Details: {1}", exp.GetType(), exp.Message);
                }

            if(string.IsNullOrEmpty(Settings.Default.ServerAddress))
            {
                Trace.TraceError("Server adress cannot be null. Setting default address to localhost.");
                Settings.Default.ServerAddress = "localhost";
            }

            base.OnStartup(e);
        }

        protected override void OnExit(ExitEventArgs e)
        {
            if(e.ApplicationExitCode == -1) // Another instance is already running
            {
                base.OnExit(e);
                return;
            }

            Trace.TraceInfo("Closing application");

            ApplicationServices.Current.Exit();
            ApplicationServices.Current.SaveData();

            // Waiting for pending updates to finish
            if (ApplicationServices.Current.States.IsBusy)
                Thread.Sleep(ApplicationServices.Current.CommunicationTimeOut);

            base.OnExit(e);
            base.DispatcherUnhandledException -= UnhandledException;

            _mutex.Dispose();
        }

        private static void ApplyCulture(string name)
        {
            try
            {
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(name);
                Trace.TraceInfo("Current Culture {0}", CultureInfo.CurrentCulture);

                WpfApplication.Resources.Messages.Culture = CultureInfo.GetCultureInfo(name);
            }
            catch (Exception exception)
            {
                Trace.TraceError("The attempt to change the current culture failed. Details: {0}", exception.Message);
            }
        }

        private static void ParseArguments(IList<string> args)
        {
            if (args.Any())
                for (var i = 0; i < args.Count; i += 2)
                {
                    if (string.Equals("-local", args[i], StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (i + 1 > args.Count) continue;
                        Settings.Default.LocalServiceUri = args[i + 1];
                        Settings.Default.Save();
                        continue;
                    }
                    if (string.Equals("-server", args[i], StringComparison.InvariantCultureIgnoreCase))
                    {
                        if(i+1 > args.Count) continue;
                        Settings.Default.ServerAddress = args[i + 1];
                        Settings.Default.Save();
                        continue;
                    }
                    if(string.Equals("-culture", args[i], StringComparison.InvariantCultureIgnoreCase))
                    {
                        if(i+1 > args.Count) continue;
                        ApplyCulture(args[i+1]);
                        continue;
                    }
                }
        }

        private void UnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            Trace.TraceException(MethodBase.GetCurrentMethod(), e.Exception, true);
            _mutex.Dispose();
        }

        private static void SetLocalUri()
        {
            var host = Dns.GetHostName();
            if (!string.IsNullOrEmpty(host))
                foreach (var address in Dns.GetHostAddresses(host)
                    .Where(t => t.AddressFamily == AddressFamily.InterNetwork))
                {
                    host = address.ToString();
                    break;
                }

            if (string.IsNullOrEmpty(host)) return;

            Settings.Default.LocalServiceUri = string.Format("http://{0}:8888/SharpTalkServices", host);
            Settings.Default.Save();
        }

        private static SourceLevels GetSettingsSourceLevel()
        {
            SourceLevels level;
            return Enum.TryParse(Settings.Default.DiagnosticTraceLevel, true, out level) 
                ? level 
                : SourceLevels.Error;
        }

        private static string GetLogFilePath()
        {
            return ".\\SharpTalk.log";
        }

        private static void SetApplicationInfos()
        {
            var asm = Assembly.GetEntryAssembly();
            _appName = Attribute.IsDefined(asm, typeof(AssemblyTitleAttribute))
                ? ((AssemblyTitleAttribute)Attribute.GetCustomAttribute(asm, typeof(AssemblyTitleAttribute))).Title
                : "SharpTalk";

            var version = asm.GetName().Version;

            _appinfos = String.Format(CultureInfo.InvariantCulture, "{0} - {1}", _appName, version);
        }
    }
}
